/**
 * Class: PFrame
 *
 * Purpose: 
 *
 * Created: Jan 21, 2010 @ 11:08:22 AM
 * @author Ken Scott
 */
package org.openfantasia.ui;

import org.openfantasia.songanalyst.controls.PControls;
import org.openfantasia.time.TimeKeeper;
import org.openfantasia.time.Timebase;
import org.openfantasia.util.Logger;
import org.openfantasia.util.TextUtils;
import org.openfantasia.util.listener.*;
import org.openfantasia.util.math.*;
import org.openfantasia.util.quicktime.QuickTimeOutputStream;
import processing.core.*;

import javax.sound.midi.InvalidMidiDataException;
import javax.sound.midi.MidiSystem;
import javax.sound.midi.Sequence;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;

public class PFrame extends PApplet implements IComponent, TimeKeeper {

	public static final double SQRT2 = PMath.SQRT2;

	public static final double SQRT2_2 = PMath.SQRT2_2;

	protected Frame window;

	protected double lineScale = 1.0;

	protected PComponent frame = new PComponent();

	protected PControls controls;

	protected ListenerManager listeners = new ListenerManager();

	protected double startTime = 0;

	protected double lastTime = 0;

	protected boolean drawing = true;

	protected boolean realtime = true;

	protected double fps = 30.0;

	protected double skewTime = 0;

	protected Timebase timebase = new Timebase();

	protected QuickTimeOutputStream quicktimeOutputStream;

	protected File movieFile;


	public static void main(String[] args) {
		PApplet.main(args);

		if (!TextUtils.containsIgnoreCase(args, "--present")) {
			Frame root = Frame.getFrames()[0];
			PFrame applet = (PFrame) root.getComponent(0);
			applet.window = root;
		}
	}


	public PFrame() {
		frame.setApplet(this);
		frame.setColor(PComponent.FILL, PColor.BLACK);
		frame.setColor(PComponent.BORDER, PColor.WHITE);
	}


	public PFrame(float width, float height) {
		frame.prep(0, 0, width, height, false);
		frame.setApplet(this);
		frame.setColor(PComponent.FILL, PColor.BLACK);
		frame.setColor(PComponent.BORDER, PColor.WHITE);
	}


	public void size(int iwidth, int iheight) {
		frame.setBounds(0, 0, iwidth, iheight);
		super.size(iwidth, iheight);
	}


	public void size(int iwidth, int iheight, String irenderer) {
		frame.setBounds(0, 0, iwidth, iheight);
		super.size(iwidth, iheight, irenderer);
	}


	public void size(int iwidth, int iheight, String irenderer, String ipath) {
		frame.setBounds(0, 0, iwidth, iheight);
		super.size(iwidth, iheight, irenderer, ipath);
	}


	public PComponent getFrame() {
		return frame;
	}


	public void startRecording(File file, double fps, double compressionQuality) throws IOException {
		movieFile = new File(file.getAbsolutePath());
		quicktimeOutputStream = new QuickTimeOutputStream(file, QuickTimeOutputStream.VideoFormat.JPG);
		quicktimeOutputStream.setTimeScale((int) fps);
		quicktimeOutputStream.setVideoCompressionQuality((float) compressionQuality);
	}


	public void stopRecording(String audioFile, double offset) {
		if (quicktimeOutputStream != null) {
			try {
				quicktimeOutputStream.close();
			}
			catch (IOException e) {
				e.printStackTrace();
			}
		}
	}


	public void addFrame() throws IOException {
		flush();
		loadPixels();
		BufferedImage img = (BufferedImage) g.getImage();
		double now = getTime();
		double delta = now - lastTime;
		double frameDuration = (delta / quicktimeOutputStream.getTimeScale());
		quicktimeOutputStream.writeFrame(img, (int) Math.max(1, frameDuration));
		lastTime = now;
		Logger.log("Added frame [" + frameCount + "]: " + now);
	}


	public Timebase getTimebase() {
		return timebase;
	}


	public void setTimebase(Timebase timebase) {
		this.timebase = timebase;
	}


	public boolean isDrawing() {
		return drawing;
	}


	public void setDrawing(boolean drawing) {
		this.drawing = drawing;
	}


	public boolean isRealtime() {
		return realtime;
	}


	public void setRealtime(boolean realtime) {
		this.realtime = realtime;
	}


	public void setTimes(double audioSkew, double start, double end) {
		this.skewTime = audioSkew;
		timebase.reset(start, end);
	}


	public double getSkewTime() {
		return skewTime;
	}


	public void setSkewTime(double skewTime) {
		this.skewTime = skewTime;
	}


	public void setStartTime(double time) {
		timebase.reset(time, timebase.getEndTime());
	}


	public double getStartTime() {
		if (timebase == null) {
			return 0;
		}
		return timebase.getStartTime();
	}


	public void setEndTime(double time) {
		timebase.reset(timebase.getStartTime(), time);
	}


	public double getEndTime() {
		if (timebase == null) {
			return 0;
		}
		return timebase.getEndTime();
	}


	public double getTime() {
		Double result;

		if (realtime) {
			result = timebase.getTime() + skewTime;
		}
		else {
			result = (double) frameCount / fps;
		}

		return result;
	}


	public double getFractionalTime() {
		if (timebase != null) {
			double time = getTime();
			double start = timebase.getStartTime();
			double end = timebase.getEndTime();
			return time / (end - start);
		}
		else {
			return 0;
		}
	}


	public void setFractionalTime(double frac) {
		if (timebase != null) {
			double start = timebase.getStartTime();
			double end = timebase.getEndTime();
			double time = frac * (end - start);
			timebase.setTime(time);
		}
	}


	public void setup() {
		colorMode(RGB, 1.0f);
		background(0f);
	}


	public void draw() {
		preDraw();

		if (isDrawing()) {
			PColor c = getColor(PComponent.FILL);
			if (c != null) {
				background(c);
			}
			drawBackground();

			drawDynamic();
			drawForeground();
			drawControls();
		}

		postDraw();

		if (quicktimeOutputStream != null) {
			try {
				addFrame();
			}
			catch (IOException e) {
				e.printStackTrace();
			}

			if (listeners.isPlaybackComplete()) {
				stop();
			}
		}
	}


	public void preDraw() {
	}


	public void postDraw() {
	}


	public void drawBackground() {
		frame.drawBackground();
	}


	public void drawDynamic() {
	}


	public void drawControls() {
		frame.drawControls();
	}


	public void drawForeground() {
		frame.drawForeground();
	}


	public PComponent getContainer() {
		return frame;
	}


	public void setContainer(PComponent container) {
		// ignored
	}


	public PApplet getApplet() {
		return this;
	}


	public void setApplet(PApplet applet) {
		// ignored
	}


	public double getLineScale() {
		return lineScale;
	}


	public void setLineScale(double lineScale) {
		this.lineScale = PMath.max(1.0, lineScale);
	}


	public void setColor(String name, PColor value) {
		frame.setColor(name, value);
	}


	public PColor getColor(String name) {
		return frame.getColor(name);
	}


	public void addBackground(PComponent child) {
		frame.addBackground(child);
	}


	public void removeBackground(PComponent child) {
		frame.removeBackground(child);
	}


	public void addForeground(PComponent child) {
		frame.addForeground(child);
	}


	public void removeForeground(PComponent child) {
		frame.removeForeground(child);
	}


	public void addControl(PComponent child) {
		frame.addControl(child);
	}


	public void removeControl(PComponent child) {
		frame.removeControl(child);
	}


	public boolean isClickThrough() {
		return false;
	}


	public void setClickThrough(boolean clickThrough) {
		// ignored
	}


	public void addListener(String category, Listener listener) {
		listeners.add(category, listener);
	}


	public void removeListener(String category, Listener listener) {
		listeners.remove(category, listener);
	}


	public void broadcast(String category, boolean value) {
		BooleanScalar event = new BooleanScalar(value);
		event.setTime(getTime());
		listeners.send(category, event);
	}


	public void broadcast(String category, float value) {
		FloatScalar event = new FloatScalar(value);
		event.setTime(getTime());
		listeners.send(category, event);
	}


	public void broadcast(String category, float[] buffer) {
		FloatArray event = new FloatArray(buffer);
		event.setTime(getTime());
		listeners.send(category, event);
	}


	public void broadcast(String category, float[][] buffer) {
		FloatGrid event = new FloatGrid(buffer);
		event.setTime(getTime());
		listeners.send(category, event);
	}


	public void broadcast(String category, float[][][] buffer) {
		FloatMatrix event = new FloatMatrix(buffer);
		event.setTime(getTime());
		listeners.send(category, event);
	}


	public void broadcast(String category, double value) {
		DoubleScalar event = new DoubleScalar(value);
		event.setTime(getTime());
		listeners.send(category, event);
	}


	public void broadcast(String category, double[] buffer) {
		DoubleArray event = new DoubleArray(buffer);
		event.setTime(getTime());
		listeners.send(category, event);
	}


	public void broadcast(String category, double[][] buffer) {
		DoubleGrid event = new DoubleGrid(buffer);
		event.setTime(getTime());
		listeners.send(category, event);
	}


	public void broadcast(String category, double[][][] buffer) {
		DoubleMatrix event = new DoubleMatrix(buffer);
		event.setTime(getTime());
		listeners.send(category, event);
	}


	public void mousePressed() {
		frame.mousePressed();
	}


	public void mouseReleased() {
		frame.mouseReleased();
	}


	public void mouseClicked() {
		frame.mouseClicked();
	}


	public void mouseDragged() {
		frame.mouseDragged();
	}


	public void mouseMoved() {
		frame.mouseMoved();
	}


	public boolean isOver() {
		return frame.isOver();
	}


	public boolean isTracking() {
		return frame.isTracking();
	}


	public boolean isPressed() {
		return frame.isPressed();
	}


	public PComponent getComponentUnderMouse() {
		return frame.getComponentUnderMouse();
	}

	// 3D support methods


	public void camera(DPoint3 cameraLocation, DPoint3 lookAt, DPoint3 up) {
		camera(cameraLocation.p[0], cameraLocation.p[1], cameraLocation.p[2], lookAt.p[0], lookAt.p[1], lookAt.p[2], up.p[0], up.p[1], up.p[2]);
	}


	public void camera(DPoint3 cameraLocation, DPoint3 lookAt) {
		camera(cameraLocation, lookAt, new DPoint3(0, -1, 0));
	}


	public void camera(DPoint3 cameraLocation) {
		camera(cameraLocation, DPoint3.ORIGIN, new DPoint3(0, -1, 0));
	}


	public void spinCamera(DPoint3 spinOrigin, DPoint3 offset, DPoint3 lookAt, double secondsPerRotation) {
		double dt = getTime() / secondsPerRotation;
		double theta = TWO_PI * PMath.frac(dt);
		DPoint3 pos = new DPoint3(Math.sin(theta), 1, Math.cos(theta));
		pos.mul(offset);
		pos.add(spinOrigin);
		camera(pos, lookAt, new DPoint3(0f, -1f, 0f));
	}


	public void swoopCamera(DPoint3 spinOrigin, DPoint3 offset, DPoint3 lookAt, double secondsPerRotation) {
		double dt = getTime() / secondsPerRotation;
		double theta = TWO_PI * PMath.frac(dt);
		DPoint3 pos = new DPoint3(Math.sin(theta), Math.sin(theta), Math.cos(theta));
		pos.mul(offset).add(spinOrigin);
		camera(pos, lookAt, new DPoint3(0f, -1f, 0f));
	}


	public void spinCamera(DPoint3 spinOrigin, DPoint3 offset, DPoint3 lookAt, double time, double min, double max, double cycleCount) {
		double theta = cycleCount * TWO_PI * (time - min) / (max - min);
		DPoint3 pos = new DPoint3(Math.sin(theta), 1, Math.cos(theta));
		pos.mul(offset).add(spinOrigin);
		camera(pos, lookAt, new DPoint3(0f, -1f, 0f));
	}

	// MIDI support


	public Sequence openMidiFile(File file) throws InvalidMidiDataException, IOException {
		return MidiSystem.getSequence(file);
	}

	// Drawing methods


	public void fill(PColor c) {
		if (c != null) {
			getApplet().fill(c.p[0], c.p[1], c.p[2], c.p[3]);
		}
	}


	public void stroke(PColor c) {
		if (c != null) {
			getApplet().stroke(c.p[0], c.p[1], c.p[2], c.p[3]);
		}
	}


	public void normal(double nx, double ny, double nz) {
		super.normal((float) nx, (float) ny, (float) nz);
	}


	public void vertex(double x, double y) {
		super.vertex((float) x, (float) y);
	}


	public void vertex(DPoint2 p) {
		super.vertex((float) p.p[0], (float) p.p[1]);
	}


	public void vertex(double x, double y, double z) {
		super.vertex((float) x, (float) y, (float) z);
	}


	public void vertex(DPoint3 p) {
		super.vertex((float) p.p[0], (float) p.p[1], (float) p.p[2]);
	}


	public void vertex(double... v) {
		float[] f = Math1D.toFloat(v);
		super.vertex(f);
	}


	public void vertex(double x, double y, double u, double v) {
		super.vertex((float) x, (float) y, (float) u, (float) v);
	}


	public void vertex(double x, double y, double z, double u, double v) {
		super.vertex((float) x, (float) y, (float) z, (float) u, (float) v);
	}


	public void bezierVertex(double x2, double y2,
			double x3, double y3,
			double x4, double y4) {
		super.bezierVertex((float) x2, (float) y2, (float) x3, (float) y3, (float) x4, (float) y4);
	}


	public void bezierVertex(double x2, double y2, double z2,
			double x3, double y3, double z3,
			double x4, double y4, double z4) {
		super.bezierVertex((float) x2, (float) y2, (float) z2, (float) x3, (float) y3, (float) z3, (float) x4, (float) y4, (float) z4);
	}


	public void curveVertex(double x, double y) {
		super.curveVertex((float) x, (float) y);
	}


	public void curveVertex(double x, double y, double z) {
		super.curveVertex((float) x, (float) y, (float) z);
	}


	public void point(double x, double y) {
		super.point((float) x, (float) y);
	}


	public void point(DPoint2 d) {
		super.point((float) d.p[0], (float) d.p[1]);
	}


	public void point(FPoint2 d) {
		super.point(d.p[0], d.p[1]);
	}


	public void point(double x, double y, double z) {
		super.point((float) x, (float) y, (float) z);
	}


	public void point(DPoint3 d) {
		super.point((float) d.p[0], (float) d.p[1], (float) d.p[2]);
	}


	public void line(double x1, double y1, double x2, double y2) {
		super.line((float) x1, (float) y1, (float) x2, (float) y2);
	}


	public void line(DPoint2 fromPt, DPoint2 toPt) {
		super.line((float) fromPt.p[0], (float) fromPt.p[1], (float) toPt.p[0], (float) toPt.p[1]);
	}


	public void line(double x1, double y1, double z1, double x2, double y2, double z2) {
		super.line((float) x1, (float) y1, (float) z1, (float) x2, (float) y2, (float) z2);
	}


	public void line(DPoint3 fromPt, DPoint3 toPt) {
		super.line((float) fromPt.p[0], (float) fromPt.p[1], (float) fromPt.p[2], (float) toPt.p[0], (float) toPt.p[1], (float) toPt.p[2]);
	}


	public void triangle(double x1, double y1, double x2, double y2, double x3, double y3) {
		super.triangle((float) x1, (float) y1, (float) x2, (float) y2, (float) x3, (float) y3);
	}


	public void triangle(DPoint2 p1, DPoint2 p2, DPoint2 p3) {
		super.line((float) p1.p[0], (float) p1.p[1], (float) p2.p[0], (float) p2.p[1], (float) p3.p[0], (float) p3.p[1]);
	}


	public void quad(double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4) {
		super.quad((float) x1, (float) y1, (float) x2, (float) y2, (float) x3, (float) y3, (float) x4, (float) y4);
	}


	public void quad(DPoint2 p1, DPoint2 p2, DPoint2 p3, DPoint2 p4) {
		super.quad((float) p1.p[0], (float) p1.p[1], (float) p2.p[0], (float) p2.p[1], (float) p3.p[0], (float) p3.p[1], (float) p4.p[0], (float) p4.p[1]);
	}


	public void rect(double x, double y, double w, double h) {
		super.rect((float) x, (float) y, (float) w, (float) h);
	}


	public void rect(DPoint2 pt, double w, double h) {
		super.rect((float) pt.p[0], (float) pt.p[1], (float) w, (float) h);
	}


	public void ellipse(double a, double b, double c, double d) {
		super.ellipse((float) a, (float) b, (float) c, (float) d);
	}


	public void ellipse(DPoint2 pt, double w, double h) {
		super.ellipse((float) pt.p[0], (float) pt.p[1], (float) w, (float) h);
	}


	public void arc(double a, double b, double c, double d, double start, double stop) {
		super.arc((float) a, (float) b, (float) c, (float) d, (float) start, (float) stop);
	}


	public void arc(DPoint2 p, double c, double d, double start, double stop) {
		super.arc((float) p.p[0], (float) p.p[1], (float) c, (float) d, (float) start, (float) stop);
	}


	public void box(double size) {
		super.box((float) size);
	}


	public void box(DPoint3 size) {
		super.box((float) size.p[0], (float) size.p[1], (float) size.p[2]);
	}


	public void box(double w, double h, double d) {
		super.box((float) w, (float) h, (float) d);
	}


	public void sphere(double r) {
		super.sphere((float) r);
	}


	public void spheric(DPoint3 size, int detail) {
		beginShape(ELLIPSE);

		double dt = detail / 2d;

		ellipse(0, 0, size.p[0], size.p[1]);
		for (double i = 0; i < dt; i++) {
			rotateY(Math.PI * i / dt);
			ellipse(0, 0, size.p[0], size.p[1]);
		}
		rotateY(-Math.PI);

		rotateX(Math.PI / 2);
		for (double i = 0; i < dt; i++) {
			rotateY(Math.PI * i / dt);
			ellipse(0, 0, size.p[0], size.p[2]);
		}
		rotateY(-Math.PI);
		rotateX(-Math.PI / 2);

		endShape(ELLIPSE);
	}
	//////////////////////////////////////////////////////////////

	// N-SIDED PYRAMID


	public void cone(double size, int faces) {
		cone(size, size, size, faces);
	}


	public void cone(DPoint3 size, int faces) {
		cone(size.p[0], size.p[1], size.p[2], faces);
	}


	public void cone(double w, double h, double d, int faces) {
		double w2 = w / SQRT2;
		double h2 = h / SQRT2;
		double d2 = d / SQRT2;
		double x0 = 0;
		double y0 = h2;
		double z0 = 0;

		// sides
		beginShape(TRIANGLES);
		for (int i = 0; i < faces; i++) {
			double ang1 = TWO_PI * (double) i / (double) faces + HALF_PI;
			double ang2 = TWO_PI * (double) (i + 1) / (double) faces + HALF_PI;
			double ang15 = TWO_PI * (double) (i + 0.5) / (double) faces + HALF_PI;

			double x1 = w2 * sin(ang1);
			double y1 = -h2;
			double z1 = d2 * cos(ang1);

			double x2 = w2 * sin(ang2);
			double y2 = -h2;
			double z2 = d2 * cos(ang2);

			double x15 = sin(ang15);
			double y15 = 0;
			double z15 = cos(ang15);
			DPoint3 n = new DPoint3(x15, y15, z15);

			normal(n.getX(), n.getY(), n.getZ());
			vertex(x0, y0, z0);
			vertex(x1, y1, z1);
			vertex(x2, y2, z2);
		}
		endShape(TRIANGLES);

		// bottom
		beginShape(POLYGON);
		normal(0, -1, 0);
		for (int i = 0; i <= faces; i++) {
			double df = (double) i / (double) faces;
			double x1 = (w2 * sin((df * TWO_PI) + HALF_PI));
			double y1 = -h2;
			double z1 = (d2 * cos((df * TWO_PI) + HALF_PI));

			vertex(x1, y1, z1);
		}
		endShape(POLYGON);
	}

	//////////////////////////////////////////////////////////////

	// N-SIDED BOX


	public void cylinder(double size, int faces) {
		cylinder(size, faces, true);
	}


	public void cylinder(double size, int faces, boolean includeEnds) {
		cylinder(size, faces, includeEnds, includeEnds);
	}


	public void cylinder(double size, int faces, boolean includeTop, boolean includeBottom) {
		cylinder(size, size, size, faces, includeTop, includeBottom);
	}


	public void cylinder(DPoint3 size, int faces) {
		cylinder(size, faces, true);
	}


	public void cylinder(DPoint3 size, int faces, boolean includeEnds) {
		cylinder(size.p[0], size.p[1], size.p[2], faces, includeEnds, includeEnds);
	}


	public void cylinder(DPoint3 size, int faces, boolean includeTop, boolean includeBottom) {
		cylinder(size.p[0], size.p[1], size.p[2], faces, includeTop, includeBottom);
	}


	public void cylinder(double w, double h, double d, int faces, boolean includeTop, boolean includeBottom) {
		double w2 = w / 2;
		double h2 = h / 2;
		double d2 = d / 2;

		// sides
		beginShape(QUADS);
		for (int i = 0; i < faces; i++) {
			double ang1 = HALF_PI + (TWO_PI * (double) i / (double) faces);
			double ang15 = HALF_PI + (TWO_PI * (double) (i + 0.5) / (double) faces);
			double ang2 = HALF_PI + (TWO_PI * (double) (i + 1) / (double) faces);

			double sin1 = sin(ang1);
			double cos1 = cos(ang1);
			double sin2 = sin(ang2);
			double cos2 = cos(ang2);

			double x1 = (w2 * sin1);
			double y1 = h2;
			double z1 = (d2 * cos1);

			double x2 = (w2 * sin1);
			double y2 = -h2;
			double z2 = (d2 * cos1);

			double x3 = (w2 * sin2);
			double y3 = -h2;
			double z3 = (d2 * cos2);

			double x4 = (w2 * sin2);
			double y4 = h2;
			double z4 = (d2 * cos2);

			double x15 = sin(ang15);
			double y15 = 0;
			double z15 = cos(ang15);
			DPoint3 n = new DPoint3(x15, y15, z15);

			normal(n.getX(), n.getY(), n.getZ());
			vertex(x1, y1, z1);
			vertex(x2, y2, z2);
			vertex(x3, y3, z3);
			vertex(x4, y4, z4);
		}
		endShape();

		// top
		if (includeTop) {
			beginShape(POLYGON);
			for (int i = 0; i <= faces; i++) {
				double df = (double) i / (double) faces;
				double x1 = (w2 * sin((df * TWO_PI) + HALF_PI));
				double y1 = h2;
				double z1 = (d2 * cos((df * TWO_PI) + HALF_PI));

				vertex(x1, y1, z1);
			}
			normal(0, 1, 0);
			endShape(POLYGON);
		}

		// bottom
		if (includeBottom) {
			beginShape(POLYGON);
			for (int i = 0; i <= faces; i++) {
				double df = (double) i / (double) faces;
				double x1 = (w2 * sin((df * TWO_PI) + HALF_PI));
				double y1 = -h2;
				double z1 = (d2 * cos((df * TWO_PI) + HALF_PI));

				vertex(x1, y1, z1);
			}
			normal(0, -1, 0);
			endShape(POLYGON);
		}
	}


	public double bezierPoint(double a, double b, double c, double d, double t) {
		return super.bezierPoint((float) a, (float) b, (float) c, (float) d, (float) t);
	}


	public double bezierTangent(double a, double b, double c, double d, double t) {
		return super.bezierTangent((float) a, (float) b, (float) c, (float) d, (float) t);
	}


	public void bezier(double x1, double y1,
			double x2, double y2,
			double x3, double y3,
			double x4, double y4) {
		super.bezier((float) x1, (float) y1, (float) x2, (float) y2, (float) x3, (float) y3, (float) x4, (float) y4);
	}


	public void bezier(DPoint2 p1, DPoint2 p2, DPoint2 p3, DPoint2 p4) {
		super.bezier((float) p1.p[0], (float) p1.p[1], (float) p2.p[0], (float) p2.p[1], (float) p3.p[0], (float) p3.p[1], (float) p4.p[0], (float) p4.p[1]);
	}


	public void bezier(double x1, double y1, double z1,
			double x2, double y2, double z2,
			double x3, double y3, double z3,
			double x4, double y4, double z4) {
		super.bezier((float) x1, (float) y1, (float) z1, (float) x2, (float) y2, (float) z2, (float) x3, (float) y3, (float) z3, (float) x4, (float) y4, (float) z4);
	}


	public void bezier(DPoint3 p1, DPoint3 p2, DPoint3 p3, DPoint3 p4) {
		super.bezier((float) p1.p[0], (float) p1.p[1], (float) p1.p[2], (float) p2.p[0], (float) p2.p[1], (float) p2.p[2], (float) p3.p[0], (float) p3.p[1], (float) p3.p[2], (float) p4.p[0], (float) p4.p[1], (float) p4.p[2]);
	}


	public double curvePoint(double a, double b, double c, double d, double t) {
		return g.curvePoint((float) a, (float) b, (float) c, (float) d, (float) t);
	}


	public double curveTangent(double a, double b, double c, double d, double t) {
		return g.curveTangent((float) a, (float) b, (float) c, (float) d, (float) t);
	}


	public void curveTightness(double tightness) {
		super.curveTightness((float) tightness);
	}


	public void curve(double x1, double y1,
			double x2, double y2,
			double x3, double y3,
			double x4, double y4) {
		super.curve((float) x1, (float) y1, (float) x2, (float) y2, (float) x3, (float) y3, (float) x4, (float) y4);
	}


	public void curve(DPoint2 p1, DPoint2 p2, DPoint2 p3, DPoint2 p4) {
		super.curve((float) p1.p[0], (float) p1.p[1], (float) p2.p[0], (float) p2.p[1], (float) p3.p[0], (float) p3.p[1], (float) p4.p[0], (float) p4.p[1]);
	}


	public void curve(double x1, double y1, double z1,
			double x2, double y2, double z2,
			double x3, double y3, double z3,
			double x4, double y4, double z4) {
		super.curve((float) x1, (float) y1, (float) z1, (float) x2, (float) y2, (float) z2, (float) x3, (float) y3, (float) z3, (float) x4, (float) y4, (float) z4);
	}


	public void curve(DPoint3 p1, DPoint3 p2, DPoint3 p3, DPoint3 p4) {
		super.curve((float) p1.p[0], (float) p1.p[1], (float) p1.p[2], (float) p2.p[0], (float) p2.p[1], (float) p2.p[2], (float) p3.p[0], (float) p3.p[1], (float) p3.p[2], (float) p4.p[0], (float) p4.p[1], (float) p4.p[2]);
	}


	public void image(PImage image, double x, double y) {
		super.image(image, (float) x, (float) y);
	}


	public void image(PImage image, DPoint2 pt) {
		super.image(image, (float) pt.p[0], (float) pt.p[1]);
	}


	public void image(PImage image, DPoint2 pt, double w, double h) {
		super.image(image, (float) pt.p[0], (float) pt.p[1], (float) w, (float) h);
	}


	public void image(PImage image, double a, double b, double c, double d, int u1, int v1, int u2, int v2) {
		super.image(image, (float) a, (float) b, (float) c, (float) d, u1, v1, u2, v2);
	}


	public void shape(PShape shape, double x, double y) {
		super.shape(shape, (float) x, (float) y);
	}


	public void shape(PShape shape, DPoint2 pt) {
		super.shape(shape, (float) pt.p[0], (float) pt.p[1]);
	}


	public void shape(PShape shape, double x, double y, double c, double d) {
		super.shape(shape, (float) x, (float) y, (float) c, (float) d);
	}


	public void shape(PShape shape, DPoint2 pt, double w, double h) {
		super.shape(shape, (float) pt.p[0], (float) pt.p[1], (float) w, (float) h);
	}


	public void textFont(PFont which, double size) {
		super.textFont(which, (float) size);
	}


	public void textLeading(double leading) {
		super.textLeading((float) leading);
	}


	public void textSize(double size) {
		super.textSize((float) size);
	}


	public void text(char c, DPoint2 pt) {
		super.text(c, (float) pt.p[0], (float) pt.p[1]);
	}


	public void text(char c, double x, double y) {
		super.text(c, (float) x, (float) y);
	}


	public void text(char c, DPoint3 pt) {
		super.text(c, (float) pt.p[0], (float) pt.p[1], (float) pt.p[2]);
	}


	public void text(String str, double x, double y) {
		super.text(str, (float) x, (float) y);
	}


	public void text(String str, DPoint2 pt) {
		super.text(str, (float) pt.p[0], (float) pt.p[1]);
	}


	public void text(char[] chars, int start, int stop, double x, double y) {
		super.text(chars, start, stop, (float) x, (float) y);
	}


	public void text(String str, double x, double y, double z) {
		super.text(str, (float) x, (float) y, (float) z);
	}


	public void text(char[] chars, int start, int stop,
			double x, double y, double z) {
		super.text(chars, start, stop, (float) x, (float) y, (float) z);
	}


	public void text(String str, double x1, double y1, double x2, double y2) {
		super.text(str, (float) x1, (float) y1, (float) x2, (float) y2);
	}


	public void text(String s, double x1, double y1, double x2, double y2, double z) {
		super.text(s, (float) x1, (float) y1, (float) x2, (float) y2, (float) z);
	}


	public void text(int num, double x, double y) {
		super.text(num, (float) x, (float) y);
	}


	public void text(int num, double x, double y, double z) {
		super.text(num, (float) x, (float) y, (float) z);
	}


	public void text(double num, double x, double y) {
		super.text((float) num, (float) x, (float) y);
	}


	public void text(double num, double x, double y, double z) {
		super.text((float) num, (float) x, (float) y, (float) z);
	}


	public void translate(double tx, double ty) {
		super.translate((float) tx, (float) ty);
	}


	public void translate(DPoint2 pt) {
		super.translate((float) pt.p[0], (float) pt.p[1]);
	}


	public void translate(double tx, double ty, double tz) {
		super.translate((float) tx, (float) ty, (float) tz);
	}


	public void translate(DPoint3 pt) {
		super.translate((float) pt.p[0], (float) pt.p[1], (float) pt.p[2]);
	}


	public void rotate(double angle) {
		super.rotate((float) angle);
	}


	public void rotate(DPoint3 angles) {
		double[] p = angles.p;
		rotateX(p[0]);
		rotateY(p[1]);
		rotateZ(p[2]);
	}


	public void rotateX(double angle) {
		super.rotateX((float) angle);
	}


	public void rotateY(double angle) {
		super.rotateY((float) angle);
	}


	public void rotateZ(double angle) {
		super.rotateZ((float) angle);
	}


	public void rotate(double angle, double vx, double vy, double vz) {
		super.rotate((float) angle, (float) vx, (float) vy, (float) vz);
	}


	public void scale(double s) {
		super.scale((float) s);
	}


	public void scale(double sx, double sy) {
		super.scale((float) sx, (float) sy);
	}


	public void scale(DPoint2 pt) {
		super.scale((float) pt.p[0], (float) pt.p[1]);
	}


	public void scale(double x, double y, double z) {
		super.scale((float) x, (float) y, (float) z);
	}


	public void scale(DPoint3 pt) {
		super.scale((float) pt.p[0], (float) pt.p[1], (float) pt.p[2]);
	}


	public void applyMatrix(double n00, double n01, double n02,
			double n10, double n11, double n12) {
		super.applyMatrix((float) n00, (float) n01, (float) n02, (float) n10, (float) n11, (float) n12);
	}


	public void applyMatrix(double n00, double n01, double n02, double n03,
			double n10, double n11, double n12, double n13,
			double n20, double n21, double n22, double n23,
			double n30, double n31, double n32, double n33) {
		super.applyMatrix((float) n00, (float) n01, (float) n02, (float) n03, (float) n10, (float) n11, (float) n12, (float) n13, (float) n20, (float) n21, (float) n22, (float) n23, (float) n30, (float) n31, (float) n32, (float) n33);
	}


	public void camera(double eyeX, double eyeY, double eyeZ,
			double centerX, double centerY, double centerZ,
			double upX, double upY, double upZ) {
		super.camera((float) eyeX, (float) eyeY, (float) eyeZ, (float) centerX, (float) centerY, (float) centerZ, (float) upX, (float) upY, (float) upZ);
	}


	public void ortho(double left, double right,
			double bottom, double top,
			double near, double far) {
		super.ortho((float) left, (float) right, (float) bottom, (float) top, (float) near, (float) far);
	}


	public void perspective() {
		if (recorder != null) {
			recorder.perspective();
		}
		g.perspective();
	}


	public void perspective(double fovy, double aspect, double zNear, double zFar) {
		super.perspective((float) fovy, (float) aspect, (float) zNear, (float) zFar);
	}


	public void frustum(double left, double right,
			double bottom, double top,
			double near, double far) {
		super.frustum((float) left, (float) right, (float) bottom, (float) top, (float) near, (float) far);
	}


	public double screenX(double x, double y) {
		return g.screenX((float) x, (float) y);
	}


	public double screenX(DPoint2 pt) {
		return g.screenX((float) pt.p[0], (float) pt.p[1]);
	}


	public double screenY(double x, double y) {
		return g.screenY((float) x, (float) y);
	}


	public double screenY(DPoint2 pt) {
		return g.screenY((float) pt.p[0], (float) pt.p[1]);
	}


	public double screenX(double x, double y, double z) {
		return g.screenX((float) x, (float) y, (float) z);
	}


	public double screenX(DPoint3 pt) {
		return g.screenX((float) pt.p[0], (float) pt.p[1], (float) pt.p[2]);
	}


	public double screenY(double x, double y, double z) {
		return g.screenY((float) x, (float) y, (float) z);
	}


	public double screenY(DPoint3 pt) {
		return g.screenY((float) pt.p[0], (float) pt.p[1], (float) pt.p[2]);
	}


	public double screenZ(double x, double y, double z) {
		return g.screenZ((float) x, (float) y, (float) z);
	}


	public double screenZ(DPoint3 pt) {
		return g.screenZ((float) pt.p[0], (float) pt.p[1], (float) pt.p[2]);
	}


	public double modelX(double x, double y, double z) {
		return g.modelX((float) x, (float) y, (float) z);
	}


	public double modelY(double x, double y, double z) {
		return g.modelY((float) x, (float) y, (float) z);
	}


	public double modelZ(double x, double y, double z) {
		return g.modelZ((float) x, (float) y, (float) z);
	}


	public void strokeWeight(float weight) {
		super.strokeWeight(weight * (float) lineScale);
	}


	public void strokeWeight(double weight) {
		super.strokeWeight((float) weight * (float) lineScale);
	}


	public void stroke(int rgb, double alpha) {
		stroke(rgb, (float) alpha);
	}


	public void stroke(double gray) {
		stroke((float) gray);
	}


	public void stroke(double gray, double alpha) {
		stroke((float) gray, (float) alpha);
	}


	public void stroke(double x, double y, double z) {
		stroke((float) x, (float) y, (float) z);
	}


	public void stroke(double x, double y, double z, double alpha) {
		stroke((float) x, (float) y, (float) z, (float) alpha);
	}


	public void tint(int rgb, double alpha) {
		tint(rgb, (float) alpha);
	}


	/**
	 * @param gray any valid number
	 */
	public void tint(double gray) {
		tint((float) gray);
	}


	public void tint(double gray, double alpha) {
		tint((float) gray, (float) alpha);
	}


	public void tint(double x, double y, double z) {
		tint((float) x, (float) y, (float) z);
	}


	public void tint(double x, double y, double z, double alpha) {
		tint((float) x, (float) y, (float) z, (float) alpha);
	}


	public void fill(int rgb, double alpha) {
		fill(rgb, (float) alpha);
	}


	public void fill(double gray) {
		fill((float) gray);
	}


	public void fill(double gray, double alpha) {
		fill((float) gray, (float) alpha);
	}


	public void fill(double x, double y, double z) {
		fill((float) x, (float) y, (float) z);
	}


	public void fill(double x, double y, double z, double alpha) {
		fill((float) x, (float) y, (float) z, (float) alpha);
	}


	public void ambient(double gray) {
		ambient((float) gray);
	}


	public void ambient(double x, double y, double z) {
		ambient((float) x, (float) y, (float) z);
	}


	public void specular(double gray) {
		specular((float) gray);
	}


	public void specular(double x, double y, double z) {
		specular((float) x, (float) y, (float) z);
	}


	public void shininess(double shine) {
		shininess((float) shine);
	}


	public void emissive(double gray) {
		emissive((float) gray);
	}


	public void emissive(double x, double y, double z) {
		emissive((float) x, (float) y, (float) z);
	}


	public void ambientLight(double red, double green, double blue) {
		ambientLight((float) red, (float) green, (float) blue);
	}


	public void ambientLight(double red, double green, double blue,
			double x, double y, double z) {
		ambientLight((float) red, (float) green, (float) blue, (float) x, (float) y, (float) z);
	}


	public void directionalLight(double red, double green, double blue,
			double nx, double ny, double nz) {
		directionalLight((float) red, (float) green, (float) blue, (float) nx, (float) ny, (float) nz);
	}


	public void pointLight(double red, double green, double blue,
			double x, double y, double z) {
		pointLight((float) red, (float) green, (float) blue, (float) x, (float) y, (float) z);
	}


	public void spotLight(double red, double green, double blue,
			double x, double y, double z,
			double nx, double ny, double nz,
			double angle, double concentration) {
		spotLight((float) red, (float) green, (float) blue, (float) x, (float) y, (float) z, (float) nx, (float) ny, (float) nz, (float) angle, (float) concentration);
	}


	public void lightFalloff(double constant, double linear, double quadratic) {
		lightFalloff((float) constant, (float) linear, (float) quadratic);
	}


	public void lightSpecular(double x, double y, double z) {
		lightSpecular((float) x, (float) y, (float) z);
	}


	public void background(int rgb, double alpha) {
		background(rgb, (float) alpha);
	}


	public void background(double gray) {
		background((float) gray);
	}


	/**
	 * @param gray	specifies a value between white and black
	 * @param alpha opacity of the background
	 */
	public void background(double gray, double alpha) {
		background((float) gray, (float) alpha);
	}


	public void background(double x, double y, double z) {
		background((float) x, (float) y, (float) z);
	}


	public void background(double x, double y, double z, double alpha) {
		background((float) x, (float) y, (float) z, (float) alpha);
	}


	public void background(PColor c) {
		float[] cc = c.p;
		background(cc[0], cc[1], cc[2], cc[3]);
	}


	public void colorMode(int mode, double max) {
		colorMode(mode, (float) max);
	}


	public void colorMode(int mode, double maxX, double maxY, double maxZ) {
		colorMode(mode, (float) maxX, (float) maxY, (float) maxZ);
	}


	public void colorMode(int mode,
			double maxX, double maxY, double maxZ, double maxA) {
		colorMode(mode, (float) maxX, (float) maxY, (float) maxZ, (float) maxA);
	}


	public int lerpColor(int c1, int c2, double amt) {
		return g.lerpColor(c1, c2, (float) amt);
	}


	static public int lerpColor(int c1, int c2, double amt, int mode) {
		return PGraphics.lerpColor(c1, c2, (float) amt, mode);
	}


	public void filter(int kind, double param) {
		filter(kind, (float) param);
	}


	public static final double sin(double angle) {
		return Math.sin(angle);
	}


	public static final double cos(double angle) {
		return Math.cos(angle);
	}


	public static final double tan(double angle) {
		return Math.tan(angle);
	}


	public static final double asin(double value) {
		return Math.asin(value);
	}


	public static final double acos(double value) {
		return Math.acos(value);
	}


	public static final double atan(double value) {
		return Math.atan(value);
	}


	public static final double atan2(double a, double b) {
		return Math.atan2(a, b);
	}


	public static final double degrees(double radians) {
		return radians * RAD_TO_DEG;
	}


	public static final double radians(double degrees) {
		return degrees * DEG_TO_RAD;
	}


	public static final int ceil(double what) {
		return (int) Math.ceil(what);
	}


	public static final int floor(double what) {
		return (int) Math.floor(what);
	}


	public static final int round(double what) {
		return (int) Math.round(what);
	}


	public static final double mag(double a, double b) {
		return Math.sqrt(a * a + b * b);
	}


	public static final double mag(double a, double b, double c) {
		return Math.sqrt(a * a + b * b + c * c);
	}


	public static final double dist(double x1, double y1, double x2, double y2) {
		return sqrt(sq(x2 - x1) + sq(y2 - y1));
	}


	public static final double dist(double x1, double y1, double z1,
			double x2, double y2, double z2) {
		return sqrt(sq(x2 - x1) + sq(y2 - y1) + sq(z2 - z1));
	}


	public static final double lerp(double start, double stop, double amt) {
		return start + (stop - start) * amt;
	}


	/**
	 * Normalize a value to exist between 0 and 1 (inclusive).
	 * Mathematically the opposite of lerp(), figures out what proportion
	 * a particular value is relative to start and stop coordinates.
	 */
	public static final double norm(double value, double start, double stop) {
		return (value - start) / (stop - start);
	}


	/**
	 * Convenience function to map a variable from one coordinate space
	 * to another. Equivalent to unlerp() followed by lerp().
	 */
	public static final double map(double value,
			double istart, double istop,
			double ostart, double ostop) {
		return ostart + (ostop - ostart) * ((value - istart) / (istop - istart));
	}


	public static final double sqrt(double a) {
		return Math.sqrt(a);
	}


	public static final double sq(double a) {
		return a * a;
	}
}
